library(dplyr)
library(knitr)
library(maptools)
library(rgdal)
library(TreeSegmentation)
library(sp)
library(ggplot2)
library(rgl)
library(clue)
library(lidR)
knit_hooks$set(webgl = hook_webgl)
opts_chunk$set(warning=F,message=F)

#set color ramp for treeID
col = pastel.colors(200)

#set data paths
path_to_tiles="../data/2017/Lidar/"

#set cores
cores<-4
#cores<-15

1 Load in ground-truth

shps<-list.files("../data/ITCs/test/",pattern=".shp",full.names = T)
itcs<-lapply(shps,readOGR,verbose=F)

names(itcs)<-sapply(itcs,function(x){
  id<-unique(x$Plot_ID)
  return(id)
  })

2 Example Pipeline

2.1 Read in Data

ground_truth<-itcs[[9]]
fname<-get_tile_filname(ground_truth)
tile<-readLAS(paste("../data/2017/Lidar/",fname,sep=""))
tile@crs<-CRS("+init=epsg:32617")
plot(tile)

You must enable Javascript to view this page properly.

2.2 Confirm overlap

plot(extent(tile),col='red')
plot(extent(ground_truth),col='blue',add=T)

2.3 Compute Segmentation

silva<-silva2016(tile=tile,output="all")
## [1] "Computing Ground Model"
## [1] "Computing Canopy Model"
## [1] "Clustering Trees"
##    user  system elapsed 
##   7.103   0.086   7.274 
## [1] "Creating tree polygons"
dalponte<-dalponte2016(tile=tile,output="all")
## [1] "Computing Ground Model"
## [1] "Computing Canopy Model"
## [1] "Clustering Trees"
##    user  system elapsed 
##   6.268   0.059   6.368 
## [1] "Creating tree polygons"
li<-li2012(tile=tile,output="all")
## [1] "Computing Ground Model"
## [1] "Computing Canopy Model"
## [1] "Clustering Trees"
##    user  system elapsed 
##   0.109   0.002   0.111 
## [1] "Creating tree polygons"
watershed_result<-watershed(tile=tile,output="all")
## [1] "Computing Ground Model"
## [1] "Computing Canopy Model"
## [1] "Clustering Trees"
##    user  system elapsed 
##   8.845   0.080   9.027 
## [1] "Creating tree polygons"

2.4 View 3d segmentation

plot(silva$tile,color="treeID",col=col)

You must enable Javascript to view this page properly.

2.4.1 Overlay ground truth and predictions

plot(silva$convex)

plot(ground_truth,col='red')
plot(silva$convex,add=T)

#plot(dalponte$convex,add=T)

2.4.2 CHM versus predicted polygons

chm=canopy_model(silva$tile)
plot(chm,ext=extent(ground_truth))
plot(ground_truth,add=T,col='red')
plot(silva$convex,add=T)

Okay that’s not great, but let’s keep going for the moment.

2.4.3 Compare Methods

Silva v Dalponte

plot(chm,ext=extent(ground_truth))
plot(silva$convex,add=T)
plot(dalponte$convex,add=T,col=rgb(0,0,255,20,maxColorValue=255))

Li versus watershed

plot(chm,ext=extent(ground_truth))

plot(li$convex)
plot(watershed_result$convex,add=T,col=rgb(0,0,255,20,maxColorValue=255))

2.5 View polygons and underlying points

for(i in 1:length(ground_truth)){
  plot(ground_truth[i,])
  plotclip<-lasclip(x=silva$tile,geometry = ground_truth[i,])
  points(cbind(plotclip@data$X,plotclip@data$Y),col=plotclip@data$X)
}

2.6 Compute consensus

ptlist<-list(silva=silva$tile,li=li$tile,dalponte=dalponte$tile,watershed=watershed_result$tile)
system.time(consensus_result<-consensus(ptlist=ptlist,method="majority"))
##    user  system elapsed 
##   7.582   0.284   7.939
consensus_polygons<-get_convex_hulls(consensus_result,consensus_result@data$treeID)
plot(consensus_polygons,col=rgb(0,0,255,20,maxColorValue=255))

paste(length(consensus_polygons),"consensus clusters found")
## [1] "248 consensus clusters found"

2.6.1 View consensus segmentation

#plot(consensus_result,color="treeID",col=col)

You must enable Javascript to view this page properly.

2.6.2 Compare Consensus

plot(silva$convex)
plot(dalponte$convex)
plot(li$convex)
plot(consensus_polygons,add=T,col=rgb(0,0,255,40,maxColorValue=255))

How many tree predictions?

library(pander)
unique_total<-sapply(c(ptlist,consensus_result),function(x) length(unique(x@data$treeID)))
df<-data.frame(Algorthm=c(names(ptlist),"consensus"),Total_Trees=as.numeric(unique_total))
pandoc.table(df,style="rmarkdown")
## 
## 
## | Algorthm  | Total_Trees |
## |:---------:|:-----------:|
## |   silva   |     248     |
## |    li     |     493     |
## | dalponte  |     249     |
## | watershed |     171     |
## | consensus |     249     |

2.7 Assign Trees

Each tree is assigned based on the maximum overlap. Pairwise membership is done using a Hungarian Algorithm. See clue::solve_LSAP.

assignment<-assign_trees(ground_truth,prediction=silva$convex)

2.8 Calculate Intersection over union

#loop through assignments and get jaccard statistic for each assignment pair
statdf<-calc_jaccard(assignment=assignment,ground_truth = ground_truth,prediction=silva$convex)
ggplot(statdf) + geom_histogram(aes(IoU)) + labs(x="Intersection over union") + theme_bw()

mean(statdf$IoU)
## [1] 0.252956
median(statdf$IoU)
## [1] 0.2244725

3 As a wrapper for one tile, multiple methods, and consensus

results<-evaluate(ground_truth=itcs[[1]],algorithm = c("silva","dalponte"),path_to_tiles=path_to_tiles,compute_consensus = F,plot_results=T)

if(!is.null(results)){
  ggplot(results,aes(y=IoU,x=Method)) + geom_boxplot() + theme_bw()
results %>% group_by(Method) %>% summarize(mean=mean(IoU),median=median(IoU))
}

4 Parallel wrapper across all tiles

system.time(results_all<-evaluate_all(itcs=itcs,algorithm = c("dalponte","li","watershed","silva"),path_to_tiles=path_to_tiles,cores=cores,extra=F,compute_consensus=F))
## [1] "ITC  has no overlap with cropped tile"
##     user   system  elapsed 
##    0.190    0.019 1185.262
#plot results
ggplot(results_all,aes(y=IoU,x=Method)) + geom_boxplot() + theme_bw()

#Compute test statistics
test_statistic<-results_all %>% group_by(Method) %>% summarize(mean=mean(IoU),median=median(IoU)) %>% mutate(Date=format(Sys.time(), "%m/%d/%y %H:%M:%S"))
test_statistic
## # A tibble: 4 x 4
##   Method     mean median Date             
##   <chr>     <dbl>  <dbl> <chr>            
## 1 dalponte  0.261  0.254 05/03/18 07:29:55
## 2 li        0.276  0.260 05/03/18 07:29:55
## 3 silva     0.266  0.252 05/03/18 07:29:55
## 4 watershed 0.221  0.211 05/03/18 07:29:55
write.table(test_statistic,"Results/results.csv",append = T,col.names = F,sep=",")